package timeDivisionMultipleAccess;

/**
 * Vorlesung: Verteilte Systeme
 * Aufgabe  : 04
 * 
 * @author Benjamin Burchard
 * @author Tell Mueller-Pettenpohl
 * 
 */

public class TimeSyncer {

	private long actVirtualTimeOffset;
	private long actArrivalTime;
	private long lastArrivalTime;
	private long lastDispatchTimestamp;

	public TimeSyncer() {
		this.actVirtualTimeOffset = 0;
		this.actArrivalTime = 0;
		this.lastArrivalTime = 0;
		this.lastDispatchTimestamp = 0;
	}

	public long getCurrentTime() {
		return System.currentTimeMillis() + this.actVirtualTimeOffset;
	}

	public long syncDispatchTime(long dispatchTime) {
		return this.lastDispatchTimestamp = dispatchTime;
	}

	public void syncTime(long timestamp, long arrivalTime) {
		// if (lastDispatchTimestamp != 0) use calculateOffsetAdvanced otherwise
		// calculateOffset
		syncArrivalTime(timestamp, arrivalTime);
		long offset = lastDispatchTimestamp != 0 /*&& actArrivalTime != 0 */ 
				? calculateOffsetAdvanced(timestamp)
				: calculateOffset(timestamp);
		actVirtualTimeOffset = checkOffest(offset, timestamp) ? offset : 0;
	}

	private void syncArrivalTime(long timestamp, long arrivalTime) {
		this.actArrivalTime = arrivalTime;
		// Accepting the actual arrivalTime is equals the last arrivalTime
		this.lastArrivalTime = lastDispatchTimestamp != 0 /*&& actArrivalTime != 0 */
				? actArrivalTime - timestamp + lastDispatchTimestamp : 0;
	}

	private long calculateOffset(long timestamp) {
		return (actArrivalTime - timestamp) >> 1L;
	}

	private long calculateOffsetAdvanced(long timestamp) {
		return (lastArrivalTime - lastDispatchTimestamp)
				- (actArrivalTime - timestamp) >> 1L;
	}

	private boolean checkOffest(long offset, long dispatchTime) {
		// if (lastArriveTime != 0) use calculateMsgDelayAdvanced otherwise
		// calculateMsgDelay
		return lastArrivalTime != 0 ? (offset - calculateMsgDelayAdvanced(dispatchTime)) < offset
				&& offset < (offset + calculateMsgDelayAdvanced(dispatchTime))
				: (offset - calculateMsgDelay(dispatchTime)) < offset
						&& offset < (offset + calculateMsgDelay(dispatchTime));
	}

	private long calculateMsgDelay(long dispatchTime) {
		return (actArrivalTime - dispatchTime) >> 1L;
	}

	private long calculateMsgDelayAdvanced(long dispatchTime) {
		return (lastArrivalTime - lastDispatchTimestamp)
				+ (actArrivalTime - dispatchTime) >> 1L;
	}
}
